using System;
using System.Collections.Generic;
using System.Linq;
using Engine;
using Engine.Graphics;
using Engine.OldSerialization;
using Color = Engine.Color;

namespace RuthlessConquest {
    public class Planet : Entity {
        public struct SentShipNotice {
            public int StepIndex;
            public Vector2 Position;
            public int Count;

            public SentShipNotice(int stepIndex, Vector2 position, int count) {
                StepIndex = stepIndex;
                Position = position;
                Count = count;
            }
        }

        //public static Dictionary<int, string> CountStrings = new();
        public static DepthStencilState RangeFillDepthStencilState = new() {
            DepthBufferFunction = CompareFunction.NotEqual, DepthBufferTestEnable = true, DepthBufferWriteEnable = true
        };

        public static DepthStencilState RangeOutlineDepthStencilState = new() {
            DepthBufferFunction = CompareFunction.NotEqual, DepthBufferTestEnable = true, DepthBufferWriteEnable = false
        };

        public Color Color1;
        public Color Color2;
        public float ColorFactor;
        public object AStarStorage;
        public float ProductionRateForDisplay; //单位：个/分钟

        public const int MaxSatellites = 2;
        public const int MaxShipsCapacity = 999;

        public Body Body { get; }

        public Point2 Position {
            get => Body.Position;
            set => Body.Position = value;
        }

        public int SizeClass { get; set; }
        public int Radius { get; set; }
        public Faction Faction { get; set; }
        public int ShipsCount { get; set; }
        public int MaxShipsCount { get; set; }
        public int ProductionPeriod { get; set; }
        public int NoProductionSteps { get; set; }
        public int DefenceFactor { get; set; }
        public bool IsSpecial { get; set; }
        public bool IsSelected { get; set; }
        public bool ShowRangeDisc { get; set; }
        public bool ShowHighlight { get; set; }
        public IEnumerable<Planet> ContinuousOrderRoute { get; set; }

        public bool IsControllingPlayerPlanet {
            get {
                if (PlayersModule.ControllingPlayer != null) {
                    return PlayersModule.ControllingPlayer.Faction == Faction;
                }
                return false;
            }
        }

        public HashSet<Satellite> _satellites = [];
        public HashSet<Satellite> Satellites => _satellites;
        public HashSet<SentShipNotice> _sentShipsNotices = [];
        public HashSet<SentShipNotice> SentShipsNotices => _sentShipsNotices;

        public Planet() {
            Body = new Body();
            Body.Tag = this;
        }

        public Planet(Point2 position, Faction faction, int sizeClass, int shipsCount, bool isSpecial) : this() {
            Position = position;
            Faction = faction;
            SizeClass = sizeClass;
            ShipsCount = shipsCount;
            IsSpecial = isSpecial;
        }

        public override void Serialize(InputArchive archive) {
            archive.Serialize("Body", Body);
            Faction = archive.Serialize<Faction>("Faction");
            SizeClass = archive.Serialize<int>("SizeClass");
            ShipsCount = archive.Serialize<int>("ShipsCount");
            NoProductionSteps = archive.Serialize<int>("NoProductionSteps");
            IsSpecial = archive.Serialize<bool>("IsSpecial");
        }

        public override void Serialize(OutputArchive archive) {
            archive.Serialize("Body", Body);
            archive.Serialize("Faction", Faction);
            archive.Serialize("SizeClass", SizeClass);
            archive.Serialize("ShipsCount", ShipsCount);
            archive.Serialize("NoProductionSteps", NoProductionSteps);
            archive.Serialize("IsSpecial", IsSpecial);
        }

        public override void OnAdded() {
            switch (SizeClass) {
                case 0:
                    MaxShipsCount = 150;
                    ProductionPeriod = 130;
                    DefenceFactor = 75;
                    break;
                case 1:
                    MaxShipsCount = 200;
                    ProductionPeriod = 90;
                    DefenceFactor = 75;
                    break;
                case 2:
                    MaxShipsCount = 250;
                    ProductionPeriod = 50;
                    DefenceFactor = 75;
                    break;
                default: throw new InvalidOperationException();
            }
            PlanetsModule planetsModule = Game.PlanetsModule;
            planetsModule.publicPlanets.Add(this);
            planetsModule.FactionOwnPlanets[(int)Faction].Add(this);
            if (IsSpecial) {
                planetsModule.SpecialPlanets.Add(this);
            }
            Body.RepelFactor = Game.CreationParameters.PlanetRepelFactor;
            Body.GridRadius = GetRadius(SizeClass) * 2;
            Radius = GetRadius(SizeClass);
            ProductionPeriod += (int)(MathUtils.Hash((uint)Position.X) % 7);
            Color1 = Color2 = GetColor(Faction);
            ColorFactor = 1f;
        }

        public override void OnRemoved() {
            PlanetsModule planetsModule = Game.PlanetsModule;
            planetsModule.publicPlanets.Remove(this);
            planetsModule.FactionOwnPlanets[(int)Faction].Remove(this);
            if (IsSpecial) {
                planetsModule.SpecialPlanets.Remove(this);
            }
        }

        public int GetSatelliteCost() {
            if (Satellites.Count() != 0) {
                return 300;
            }
            return 150;
        }

        public bool CanLaunchSatellite(bool ignoreCost) {
            if (Satellites.Count() < 2) {
                if (!ignoreCost) {
                    return ShipsCount >= GetSatelliteCost();
                }
                return true;
            }
            return false;
        }

        public void LaunchSatellite() {
            if (CanLaunchSatellite(false)) {
                ShipsCount -= GetSatelliteCost();
                Game.AddEntity(new Satellite(this, 0));
                AudioManager.PlaySound(Sounds.SatelliteLaunch, true);
            }
        }

        public void ChangeOwner(Faction faction) {
            Game.PlanetsModule.FactionOwnPlanets[(int)Faction].Remove(this);
            Game.PlanetsModule.FactionOwnPlanets[(int)faction].Add(this);
            if (PlayersModule.ControllingPlayer != null) {
                if (Faction == PlayersModule.ControllingPlayer.Faction
                    && faction != PlayersModule.ControllingPlayer.Faction) {
                    AudioManager.PlaySound(Sounds.PlanetLost, true);
                }
                else if (Faction != PlayersModule.ControllingPlayer.Faction
                    && faction == PlayersModule.ControllingPlayer.Faction) {
                    AudioManager.PlaySound(Sounds.PlanetWon, true);
                }
            }
            if (ParticlesModule.ParticleSystems.Count(p => p is CapturePlanetParticleSystem) < 5) {
                ParticlesModule.AddParticleSystem(new CapturePlanetParticleSystem(new Vector2(Position), Radius, Ship.GetColor(faction)));
            }
            Faction = faction;
            NoProductionSteps = Game.CreationParameters.NoProductionSteps;
            IsSelected = false;
            Color1 = Color.Lerp(Color1, Color2, ColorFactor);
            Color2 = GetColor(faction);
            ColorFactor = 0f;
            ContinuousOrderRoute = null;
            Satellite[] array = Satellites.ToArray();
            foreach (Satellite entity in array) {
                Game.RemoveEntity(entity);
            }
        }

        public void Step() {
            if (NoProductionSteps <= 0) {
                Player player = PlayersModule.FindPlayer(Faction);
                int num = player == null ? ProductionPeriod * 7 :
                    player.FactionProductionFactor > 0 ? ProductionPeriod * 100 / player.FactionProductionFactor : int.MaxValue;
                if (ShipsCount < MaxShipsCount) {
                    if (StepModule.StepIndex % num == num - 1) {
                        ShipsCount++;
                    }
                    //不直接用num是因为玩家看不到ProductionPeriod随机加的0~6
                    ProductionRateForDisplay = SizeClass switch {
                        0 => 3600f / 133f,
                        1 => 3600f / 93f,
                        2 => 3600f / 53f,
                        _ => 0
                    };
                    if (player == null) {
                        ProductionRateForDisplay /= 7f;
                    }
                    else {
                        ProductionRateForDisplay *= player.FactionProductionFactor / 100f;
                    }
                }
            }
            else {
                NoProductionSteps--;
                ProductionRateForDisplay = 0f;
            }
            if (ShipsCount > MaxShipsCount) {
                int num2 = 100 * (ShipsCount - MaxShipsCount) / MaxShipsCount;
                int num3 = num2 < 150 ? 90 :
                    num2 >= 250 ? 40 : 60;
                if (StepModule.StepIndex % num3 == 0) {
                    ShipsCount--;
                }
                ProductionRateForDisplay = ShipsCount == MaxShipsCount ? 0f : -3600f / num3;
            }
            if (ContinuousOrderRoute != null
                && ShipsCount > 0
                && PlayersModule.FindPlayer(Faction) is HumanPlayer humanPlayer) {
                int num4 = 120 + PlanetsModule.Planets.IndexOf(this);
                if (StepModule.StepIndex % num4 == 0) {
                    humanPlayer.AddOrderToOutgoingList(this, ContinuousOrderRoute, ShipsCount, false, Faction.None);
                }
            }
        }

        public void DrawPlanet(Color colorTransform) {
            Texture2D texture = IsSpecial ? Textures.Planet2 : Textures.Planet;
            TexturedBatch2D texturedBatch2D = CameraModule.PrimitivesRenderer.TexturedBatch(texture, false, 0, null, null, BlendState.AlphaBlend);
            float v = 1.7f * Radius;
            Color color = Color.Lerp(Color1, Color2, ColorFactor) * colorTransform;
            ColorFactor = MathUtils.Min(ColorFactor + 1f * Time.FrameDuration, 1f);
            texturedBatch2D.QueueQuad(
                new Vector2(Position) - new Vector2(v),
                new Vector2(Position) + new Vector2(v),
                0f,
                Vector2.Zero,
                Vector2.One,
                color
            );
        }

        public void DrawOverlays(Color colorTransform) {
            FlatBatch2D flatBatch2D = CameraModule.PrimitivesRenderer.FlatBatch(1);
            FontBatch2D fontBatch2D = CameraModule.PrimitivesRenderer.FontBatch(
                global::Game.LabelWidget.BitmapFont,
                2,
                null,
                null,
                BlendState.AlphaBlend
            );
            float num = Radius;
            Color color = Color.White * colorTransform;
            if (IsSelected) {
                float num2 = 90f;
                float num3 = 70f;
                flatBatch2D.QueueDisc(
                    new Vector2(Position),
                    new Vector2(num - num3),
                    new Vector2(num - num2),
                    0f,
                    color,
                    Color.Transparent,
                    48
                );
                flatBatch2D.QueueDisc(
                    new Vector2(Position),
                    new Vector2(num + num3),
                    new Vector2(num - num3),
                    0f,
                    color,
                    color,
                    48
                );
                flatBatch2D.QueueDisc(
                    new Vector2(Position),
                    new Vector2(num + num2),
                    new Vector2(num + num3),
                    0f,
                    Color.Transparent,
                    color,
                    48
                );
            }
            else if (ShowHighlight) {
                float num4 = 30f;
                float num5 = 10f;
                flatBatch2D.QueueDisc(
                    new Vector2(Position),
                    new Vector2(num - num5),
                    new Vector2(num - num4),
                    0f,
                    color,
                    Color.Transparent,
                    48
                );
                flatBatch2D.QueueDisc(
                    new Vector2(Position),
                    new Vector2(num + num5),
                    new Vector2(num - num5),
                    0f,
                    color,
                    color,
                    48
                );
                flatBatch2D.QueueDisc(
                    new Vector2(Position),
                    new Vector2(num + num4),
                    new Vector2(num + num5),
                    0f,
                    Color.Transparent,
                    color,
                    48
                );
            }
            else if (IsControllingPlayerPlanet) {
                if (PlayersModule.ControllingPlayer.BlinkPlanets) {
                    if (MathUtils.Remainder(Time.FrameStartTime * 2.0, 1.0) < 0.5) {
                        float num6 = 50f;
                        float num7 = 30f;
                        flatBatch2D.QueueDisc(
                            new Vector2(Position),
                            new Vector2(num - num7),
                            new Vector2(num - num6),
                            0f,
                            color,
                            Color.Transparent,
                            48
                        );
                        flatBatch2D.QueueDisc(
                            new Vector2(Position),
                            new Vector2(num + num7),
                            new Vector2(num - num7),
                            0f,
                            color,
                            color,
                            48
                        );
                        flatBatch2D.QueueDisc(
                            new Vector2(Position),
                            new Vector2(num + num6),
                            new Vector2(num + num7),
                            0f,
                            Color.Transparent,
                            color,
                            48
                        );
                    }
                }
                else {
                    float num8 = 20f;
                    flatBatch2D.QueueDisc(
                        new Vector2(Position),
                        new Vector2(num - 0f),
                        new Vector2(num - num8),
                        0f,
                        color,
                        Color.Transparent,
                        48
                    );
                    flatBatch2D.QueueDisc(
                        new Vector2(Position),
                        new Vector2(num + num8),
                        new Vector2(num + 0f),
                        0f,
                        Color.Transparent,
                        color,
                        48
                    );
                }
            }
            if (IsControllingPlayerPlanet || Faction == Faction.Neutral) {
                Player player = PlayersModule.FindPlayer(Faction);
                Color color2;
                Color color3;
                if (player != null) {
                    float f = MathUtils.Saturate(MathUtils.Min(1f - NoProductionSteps * 0.05f, player.FactionProductionFactor / 100f));
                    color2 = Color.Lerp(
                        Color.Lerp(new Color(100, 100, 100), Color.White, f) * colorTransform,
                        Color.Red,
                        MathUtils.Saturate((ShipsCount - MaxShipsCount) / (float)MaxShipsCount)
                    );
                    color3 = Color.Black * colorTransform;
                }
                else {
                    color2 = Color.White;
                    color3 = Color.Black;
                }
                color2 *= colorTransform;
                color3 *= colorTransform;
                /*if (!CountStrings.TryGetValue(ShipsCount, out string value)) {
                    value = ShipsCount.ToString();
                    CountStrings.Add(ShipsCount, value);
                }*/
                string countString = ShipsCount.ToString();
                TextAnchor anchor = SettingsManager.DisplayProductionRateOfPlanets
                    ? TextAnchor.Bottom | TextAnchor.HorizontalCenter
                    : TextAnchor.Center;
                fontBatch2D.QueueText(
                    countString,
                    new Vector2(Position) + new Vector2(20f),
                    0f,
                    color3,
                    anchor,
                    new Vector2(9.5f),
                    new Vector2(0f, 0f)
                );
                fontBatch2D.QueueText(
                    countString,
                    new Vector2(Position),
                    0f,
                    color2,
                    anchor,
                    new Vector2(9.5f),
                    new Vector2(0f, 0f)
                );
                if (SettingsManager.DisplayProductionRateOfPlanets) {
                    string extraString;
                    if (NoProductionSteps > 0) {
                        extraString = $"{NoProductionSteps / 60f:0}s";
                    }
                    else {
                        bool small = Math.Abs(ProductionRateForDisplay) < 10;
                        extraString = $"{ProductionRateForDisplay.ToString(small ? "+0.0;-#;0" : "+0;-#;0")}/m";
                    }
                    fontBatch2D.QueueText(
                        extraString,
                        new Vector2(Position),
                        0f,
                        color2,
                        TextAnchor.HorizontalCenter,
                        new Vector2(7.5f),
                        new Vector2(0f, 0f)
                    );
                }
            }
            if (NoProductionSteps > 0
                && !IsControllingPlayerPlanet
                && Faction != Faction.Neutral
                && SettingsManager.DisplayProductionRateOfPlanets) {
                fontBatch2D.QueueText(
                    $"{NoProductionSteps / 60f:0}s",
                    new Vector2(Position),
                    0f,
                    Color.Gray,
                    TextAnchor.Center,
                    new Vector2(7.5f),
                    new Vector2(0f, 0f)
                );
            }
            if (ShowRangeDisc && Game.CreationParameters.ShipRange < 20000) {
                DrawingUtils.QueueRangeDisc(
                    CameraModule.PrimitivesRenderer.FlatBatch(2, RangeFillDepthStencilState),
                    new Vector2(Position),
                    Game.CreationParameters.ShipRange,
                    0.5f,
                    Color.White * 0.07f,
                    GetColor(Faction) * 0.3f,
                    false
                );
                DrawingUtils.QueueRangeDisc(
                    CameraModule.PrimitivesRenderer.FlatBatch(3, RangeOutlineDepthStencilState),
                    new Vector2(Position),
                    Game.CreationParameters.ShipRange,
                    0.5f,
                    Color.White * 0.07f,
                    GetColor(Faction) * 0.3f,
                    true
                );
            }
            if (SettingsManager.DisplayMoreNotices
                && SentShipsNotices.Count > 0) {
                SentShipsNotices.RemoveWhere(notice => StepModule.StepIndex - notice.StepIndex > 150);
                foreach (SentShipNotice notice in SentShipsNotices) {
                    fontBatch2D.QueueText(
                        notice.Count.ToString(),
                        notice.Position,
                        0f,
                        Color.White,
                        TextAnchor.Center,
                        new Vector2(9.5f),
                        new Vector2(0f, 0f)
                    );
                }
            }
            if (!ShowHighlight
                || !IsControllingPlayerPlanet
                || Game.CreationParameters.ShipRange >= 20000) {
                return;
            }
            FlatBatch2D batch = CameraModule.PrimitivesRenderer.FlatBatch(-1);
            foreach (Planet item in PlanetsModule.Planets.Where(p => ShipsModule.TestReachability(p.Position, Position))) {
                if (!item.IsControllingPlayerPlanet
                    || PlanetsModule.Planets.IndexOf(item) > PlanetsModule.Planets.IndexOf(this)) {
                    Vector2 p2 = new(Position);
                    Vector2 p3 = new(item.Position);
                    DrawingUtils.QueueThickLine(
                        batch,
                        p2,
                        p3,
                        0f,
                        20f,
                        0f,
                        20f,
                        0.3f * GetColor(Faction),
                        Color.Transparent
                    );
                }
            }
        }

        public static Color GetColor(Faction faction) {
            return faction switch {
                Faction.Faction1 => new Color(230, 25, 75),
                Faction.Faction2 => new Color(245, 130, 48),
                Faction.Faction3 => new Color(255, 220, 0),
                Faction.Faction4 => new Color(60, 200, 75),
                Faction.Faction5 => new Color(0, 140, 255),
                Faction.Faction6 => new Color(240, 50, 255),
                _ => Color.DarkGray
            };
        }

        public static int GetRadius(int sizeClass) {
            return sizeClass switch {
                0 => 300,
                1 => 400,
                2 => 500,
                _ => throw new InvalidOperationException()
            };
        }
    }
}